use std::{fmt::Display, fs::metadata, path::{Path, PathBuf}, time::SystemTime};

use clap::Args;
use serde::{Deserialize, Serialize};



#[derive(Debug, Clone, Serialize, Deserialize, Args)]
pub struct FileUDArgs {
    /// 文件分块传输，每次传输大小
    #[arg(long, default_value = "16777216")]
    pub chunk_size: usize,

    /// 文件是否能被覆盖
    #[arg(long)]
    pub overwrite: bool,
    
    /// 上传或下载的文件路径，如果上传则为本地文件路径，如果下载则为远程文件绝对路径
    pub file: PathBuf,
}

impl FileUDArgs {
    pub fn new(file: PathBuf) -> Self {
        Self {
            file,
            chunk_size: 1024000,
            overwrite: false,
        }
    }
}



#[derive(Debug, Deserialize, Serialize)]
pub struct PathNode {
    pub path: String,
    pub len: u64,
    pub motified: SystemTime,
    pub is_dir: bool,
    pub children: Vec<PathNode>,
}

impl PathNode {
    pub fn new(path: &Path, deep: u8, hidden: Option<&str>) -> Self {
        if !path.exists() {
            return Self {
                path: path.file_name().unwrap().to_string_lossy().to_string(),
                len: 0,
                motified: SystemTime::now(),
                is_dir: false,
                children: Vec::with_capacity(0),
            };
        }
        
        let is_dir = path.is_dir();
        let children = if deep == 0 {
            Vec::with_capacity(0)
        } else if let Ok(node) = path.read_dir() {
            if let Some(hidden) = hidden {
                node.filter_map(|x| {
                    let path = x.unwrap().path();
                    if path.file_name().unwrap().to_str().unwrap().starts_with(hidden) {
                        None
                    } else {
                        Some(Self::new(&path, deep-1, Some(hidden)))
                    }
                }).collect::<Vec<_>>()
            } else {
                node.map(|x| {
                    let path = x.unwrap().path();
                    Self::new(&path, deep-1, hidden)
                }).collect::<Vec<_>>()
            }
        } else {
            Vec::with_capacity(0)
        };
        let meta = metadata(path).unwrap();
        let len = if is_dir {
            0
        } else {
            meta.len()
        };
        Self {
            path: path.file_name().unwrap().to_string_lossy().to_string(),
            len,
            motified: meta.modified().unwrap(),
            is_dir,
            children,
        }
    }

    pub(super) fn children_to_string(&self, prefix: &str) -> Vec<String> {
        
        self.children.iter().flat_map(| v | {
            let mut nodes_children = v.children_to_string(&format!("  {prefix}"));
            nodes_children.insert(0, format!("{}\t{}{}", v.is_dir, prefix, v.path));
            nodes_children
        }).collect::<Vec<_>>()
    }
}

impl Display for PathNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let nodes = self.children_to_string("").join("\n");
        write!(f, "is_dir\tname\n{nodes}")
    }
}







